package data

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/redis/go-redis/v9"
	"log/slog"
	"rule/cache"
	"rule/control"
	"rule/model"
	"strconv"
	"time"
)

var ctx = context.Background()

// 反控对外 暴露的函数 具体实现全写内部
func reverseCtrl(id, ruleId string, linkageList []model.LinkagePoint) {

	// 挑选数据分2个部分, TriggerInterval = 0 是立马执行的数据, 大于0是延迟执行的
	directlyDataArray := make([]model.LinkagePoint, 0, len(linkageList))
	delayedDataMap := make(map[int64][]model.LinkagePoint)
	// 反控点位的 ptId
	ptIds := make([]string, 0, len(linkageList))

	for _, _linkage := range linkageList {
		ptIds = append(ptIds, _linkage.PtId)
		//
		if _linkage.TriggerInterval == 0 {
			// 立刻点位
			directlyDataArray = append(directlyDataArray, _linkage)
		} else if _linkage.TriggerInterval > 0 {
			// 延迟点位
			arr, ok := delayedDataMap[_linkage.TriggerInterval]
			if !ok {
				delayedDataMap[_linkage.TriggerInterval] = []model.LinkagePoint{_linkage}
			} else {
				delayedDataMap[_linkage.TriggerInterval] = append(arr, _linkage)
			}
		}
	}

	_now_ms := time.Now().UnixMilli() // 毫秒 13位时间戳

	go func() {
		var er2 error
		batchId := fmt.Sprintf("%s_%d", ruleId, _now_ms)
		_, er2 = cache.Rdb0.HSet(ctx, "ctrl:batch_id:count", batchId, len(ptIds)).Result()
		if er2 != nil {
			slog.Error("HSet_失败_ctrl:bid:"+batchId, "error", er2.Error())
		}
		// _, _ = cache.Rdb0.RPush(ctx, "batchId:"+batchId, ptIds).Result()
		_, er2 = cache.Rdb0.SAdd(ctx, "ctrl:bid:"+batchId, ptIds).Result()
		if er2 != nil {
			slog.Error("SAdd_失败_ctrl:bid:"+batchId, "error", er2.Error())
		}
		er2 = nil
	}()

	// 立刻
	if len(directlyDataArray) > 0 {
		control.ReverseCtrl(id, ruleId, _now_ms, 0, directlyDataArray)
	}

	// 延迟队列 redis sorted set
	if len(delayedDataMap) > 0 {
		// 延迟间隔 和 数据
		for delayInterval, delayDatas := range delayedDataMap {
			// 计算执行时间
			_execms := _now_ms + delayInterval*1000
			// 生成 redis唯一task_id
			_taskid := fmt.Sprintf("ctrl_task_id_%s_%s_%d_%d", id, ruleId, delayInterval, _now_ms)
			//
			taskStruct := &model.DelayCtrlTaskStruct{id, ruleId, _now_ms, delayInterval, delayDatas}
			//
			buf, _ := json.Marshal(taskStruct)
			bufStr := string(buf)
			go func() {
				_, err := cache.Rdb0.Set(ctx, _taskid, bufStr, 0).Result()
				if err != nil {
					slog.Error("反控添加延迟任务失败_set", "error", err.Error())
				}
				_, err = cache.Rdb0.ZAdd(ctx, "ctrl_task_list", redis.Z{
					Score:  float64(_execms),
					Member: _taskid,
				}).Result()
				if err != nil {
					slog.Error("反控添加延迟任务失败_zadd", "error", err.Error())
				}
			}()
		}
	}
}

// 延迟任务, 延迟反控 读取redis的数据
func processDelayTask() {
	_nowms := time.Now().UnixMilli() // 毫秒 13位时间戳
	_nowmsstr := strconv.FormatInt(_nowms, 10)

	tasks, err := cache.Rdb0.ZRangeByScore(ctx, "ctrl_task_list", &redis.ZRangeBy{
		Min: "0",
		Max: _nowmsstr,
	}).Result()

	if err != nil {
		//  没有任务
		return
	}

	for _, taskId := range tasks {
		err = execTask(taskId)
		if err != nil {
			slog.Error("执行任务失败", "task_id", taskId, "error", err.Error())
		}
	}

}

// 执行任务
func execTask(taskId string) error {
	taskJsonBytes, err := cache.Rdb0.Get(ctx, taskId).Bytes()
	if err != nil {
		return errors.New("redis读取task失败" + taskId + err.Error())
	}
	//
	var taskStruct model.DelayCtrlTaskStruct
	err = json.Unmarshal(taskJsonBytes, &taskStruct)
	if err != nil {
		return errors.New("json_unmarshal_err" + taskId + err.Error())
	}
	ruleId := taskStruct.RuleId
	//
	slog.Warn("exec_delay_task触发延迟任务", taskId, ruleId, "task_struct", taskStruct)
	control.ReverseCtrl(taskStruct.Id, ruleId, taskStruct.Ts, taskStruct.DelaySec, taskStruct.Linkages)
	// 执行成功了, 清除redis数据
	cache.Rdb0.ZRem(ctx, "ctrl_task_list", taskId)
	cache.Rdb0.Del(ctx, taskId)
	return nil
}
